
#include <spu_intrinsics.h>
#include <spu_mfcio.h>
//#include <libspe2.h>
#include "cmatrixprod.h"
#include <string.h>

/* Variables Globales */

#define MAX_BUFSIZE (4096)

t_complex in_A[MAX_BUFSIZE] __attribute__((aligned(128)));
t_complex in_B[MAX_BUFSIZE] __attribute__((aligned(128)));
t_complex in_A2[MAX_BUFSIZE] __attribute__((aligned(128)));
t_complex in_B2[MAX_BUFSIZE] __attribute__((aligned(128)));
t_complex out_C[MAX_BUFSIZE] __attribute__((aligned(128)));
t_complex out_C2[MAX_BUFSIZE] __attribute__((aligned(128)));

uint64_t next_in_A ;
uint64_t next_in_B ;

int tag = 1, tid = 0, rid = 0,size_b,nbiter;
unsigned int res;
uint32_t adrl,adrh,spenumber;

abs_params_t abs_params __attribute__((aligned(16)));

/* Fonction d'affichage */

void affiche(t_complex a[], int nlig, int ncol, char *s)
{
  int i,j;
  printf("__SPE\n\n%s=[\n",s);
  
  for (i=0; i<nlig;i++) {
    for (j=0;j<ncol;j++) 
      printf("%f + %f*i  ",a[i*ncol+j].r,a[i*ncol+j].i);
    printf(";\n"); 
  }   
  printf("];\n");
}


/* Fonction de calculation de multiplication complexe */

void calculation(int M, int N, int P, t_complex *A, 
		 t_complex *B, t_complex *C){
  int i,j,k;
  t_complex som;
  for(i=0;i<M;i++){
    for (j=0;j<N;j++){
      som=C[i*N+j];
      for (k=0;k<P;k++) {
	som.r = som.r + A[i*P+k].r*B[k*N+j].r-A[i*P+k].i*B[k*N+j].i;
	som.i = som.i + A[i*P+k].r*B[k*N+j].i+A[i*P+k].i*B[k*N+j].r;
      }
      C[i*N+j]=som;
    }
  }
}


int main(unsigned long long spe, unsigned long long argp, unsigned long long envp)
{
  int i,j,k;
  int tag_mask=1<<tag;
  char *s;
  nbiter=NUM_SPE/2;
 
  /* on recupere un numero de thread */
  while (spu_stat_in_mbox()==0);
  spenumber=spu_read_in_mbox();
  
  /*initialisation de l'anneau entre spe*/
  
  /*envoi de l'adresse de ma variable au PPE*/
  while((spu_stat_out_mbox())==0);
  spu_write_out_mbox((uint32_t) in_A);
  
  while((spu_stat_out_mbox())==0);
  spu_write_out_mbox((uint32_t) in_B);
  
  /*reception de l'adresse du contenant sur le SPE suivant*/
  while(spu_stat_in_mbox()==0);
  adrh=spu_read_in_mbox();
  while(spu_stat_in_mbox()==0);
  adrl=spu_read_in_mbox();
  next_in_A=mfc_hl2ea(adrh,adrl);
  //printf("SPE %d: adresse du prochain spe= %llx\n",spenumber,next_in_A);
  
  /*reception de l'adresse du contenant sur le SPE suivant*/
  while(spu_stat_in_mbox()==0);
  adrh=spu_read_in_mbox();
  while(spu_stat_in_mbox()==0);
  adrl=spu_read_in_mbox();
  next_in_B=mfc_hl2ea(adrh,adrl);
  //printf("SPE %d: adresse du prochain spe= %llx\n",spenumber,next_in_B);
  
  /* DMA Transfer 1 : GET input/output parameters */
  mfc_get(&abs_params, argp, sizeof(abs_params_t), tag, tid, rid);
  mfc_write_tag_mask(tag_mask);
  res = mfc_read_tag_status_all();
  
  size_b=abs_params.size_block;
  
  /*bloque ou_C chargé uniquement au debut*/
  for(i=0;i<size_b;i++){
    mfc_get( out_C+(i*size_b), 
	     abs_params.out_matrix_C+
	     (i*abs_params.size_P*sizeof(t_complex))+
	     (abs_params.size_P*spenumber*size_b*sizeof(t_complex))+
	     (spenumber*size_b*sizeof(t_complex)), 
	     size_b * sizeof(t_complex), 
	     tag, tid, rid);
  }
     
  for(j=0;j<NUM_SPE;j++){/*boucle principale*/
     for(i=0;i<size_b;i++){
      mfc_get( in_A2+(i*size_b),
	       abs_params.in_matrix_A+
	       (i*abs_params.size_M*sizeof(t_complex))+
	       (abs_params.size_M*spenumber*size_b*sizeof(t_complex))+
	       (j*size_b*sizeof(t_complex)),
	       size_b * sizeof(t_complex),
	       tag,tid,rid);
    } 
    
     for(i=0;i<size_b;i++){
      mfc_get( in_B2+(i*size_b), 
	       abs_params.in_matrix_B+
	       (i*abs_params.size_N*sizeof(t_complex))+
	       (abs_params.size_N*j*size_b*sizeof(t_complex))+
	       (spenumber*size_b*sizeof(t_complex)), 
	       size_b * sizeof(t_complex), 
	       tag, tid, rid);
    }
    /*waiting for data*/
    mfc_write_tag_mask(1<<tag);
    mfc_read_tag_status_all();
    
    /* Calcul de la multiplication */
    calculation(size_b,size_b,size_b, in_A2, in_B2, out_C);
    
    /*traitement des transferts de donnees sur l'anneau inter spe*/
    for(k=1;k<=nbiter;k++){
      int p;      
      if(k==1){
	for(p=0;p<=1;p++){
	  mfc_put(in_A2+(size_b*(size_b/2)*p),
		  next_in_A+((p*size_b*(size_b/2))*sizeof(t_complex)),
		  size_b*(size_b/2)*sizeof(t_complex),
		  tag,tid,rid); 
	}
	
	for(p=0;p<2;p++){
	  mfc_put( in_B2+(size_b*(size_b/2)*p), 
		   next_in_B+((p*size_b*(size_b/2))*sizeof(t_complex)),
		   size_b * (size_b/2)*sizeof(t_complex), 
		   tag+1, tid, rid);
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();

      }else{
	memcpy(out_C2,in_A,size_b*size_b*sizeof(t_complex));
	
	/*synchro on attend que tout les spe ai copié leur donné pour ne pas l'ecraser*/
	while (spu_stat_out_mbox()==0);
	spu_write_out_mbox(k);
	while (spu_stat_in_mbox()==0);
	spu_read_in_mbox();
	
	/*petit tour de magie pour ne pas succomber au problème de l'oeuf et la poule de l'anneau*/
	for(p=0;p<2;p++){
	  mfc_put(out_C2+(size_b*(size_b/2)*p), 
		  next_in_A+((p*size_b*(size_b/2))*sizeof(t_complex)),
		  size_b*(size_b/2)*sizeof(t_complex),
		  tag,tid,rid); 
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();

	/*synchro avec le ppe*/
	while (spu_stat_out_mbox()==0);
	spu_write_out_mbox(k);
	while (spu_stat_in_mbox()==0);
	spu_read_in_mbox();
	
	memcpy(out_C2,in_B,size_b*size_b*sizeof(t_complex));

	/*synchro avec le ppe*/
	while (spu_stat_out_mbox()==0);
	spu_write_out_mbox(k);
	while (spu_stat_in_mbox()==0);
	spu_read_in_mbox();

	for(p=0;p<2;p++){
	  mfc_put( out_C2+(size_b*(size_b/2)*p),  
		   next_in_B+((p*size_b*(size_b/2))*sizeof(t_complex)),
		   size_b * (size_b/2)*sizeof(t_complex), 
		   tag+1, tid, rid);
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();
      }
      
      /*synchro des spe*/
      while (spu_stat_out_mbox()==0);
      spu_write_out_mbox(k+1);
      while (spu_stat_in_mbox()==0);
      spu_read_in_mbox();
      
      /***************************************/
      
      /*if((spenumber==0)||(spenumber==4)||(spenumber==5)){
	sprintf(s,"SPE %d: tour %d --- in_A2",spenumber,k);
	affiche(in_A2, size_b, size_b, s);
	sprintf(s,"SPE %d: tour %d --- in_A",spenumber,k);
	affiche(in_A, size_b, size_b, s);
	}*/
    
      if((k==nbiter)&&(spenumber>=nbiter)){}/*inibe les calcules redondants*/
      else{
        
	/* ecriture du bloc C intermediaire precedent et chargement du suivant */
	for(i=0;i<size_b;i++){ 
	  mfc_get( out_C2+(i*size_b), 
		   abs_params.out_matrix_C+
		   (i*abs_params.size_P*sizeof(t_complex))+
		   (abs_params.size_P*(((spenumber+k)*size_b)%abs_params.size_P)*sizeof(t_complex))+
		   (spenumber*size_b*sizeof(t_complex)), 
		   size_b * sizeof(t_complex), 
		   tag, tid, rid);
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();
	
	/* Calcul de la multiplication intermediaire */
      	calculation(size_b,size_b,size_b, in_A, in_B2, out_C2);
	
	/* ecriture du bloc C intermediaire precedent et chargement du suivant */
	for(i=0;i<size_b;i++){
	  mfc_put( out_C2+(i*size_b), 
		   abs_params.out_matrix_C+
		   (i*abs_params.size_P*sizeof(t_complex))+
		   (abs_params.size_P*(((spenumber+k)*size_b)%abs_params.size_P)*sizeof(t_complex))+
		   (spenumber*size_b*sizeof(t_complex)),
		   size_b * sizeof(t_complex), 
		   tag, tid, rid);
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();
	
	
	/* ecriture du bloc C intermediaire precedent et chargement du suivant */
	for(i=0;i<size_b;i++){
	  mfc_get( out_C2+(i*size_b), 
		   abs_params.out_matrix_C+
		   (i*abs_params.size_P*sizeof(t_complex))+
		   ((abs_params.size_P*spenumber*size_b)*sizeof(t_complex))+
		   ((((spenumber+k)*size_b)%abs_params.size_P)*sizeof(t_complex)), 
		   size_b * sizeof(t_complex), 
		   tag, tid, rid);
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();
	
	/* Calcul de la multiplication intermediaire */
	calculation(size_b,size_b,size_b, in_A2, in_B, out_C2);
	
	for(i=0;i<size_b;i++){ 
	  mfc_put( out_C2+(i*size_b), 
		   abs_params.out_matrix_C+
		   (i*abs_params.size_P*sizeof(t_complex))+
		   ((abs_params.size_P*spenumber*size_b)*sizeof(t_complex))+
		   ((((spenumber+k)*size_b)%abs_params.size_P)*sizeof(t_complex)), 
		   size_b * sizeof(t_complex), 
		   tag, tid, rid);
	}
	mfc_write_tag_mask(1<<tag);
	mfc_read_tag_status_all();
      }
      /*synchro spe*/
      while (spu_stat_out_mbox()==0);
      spu_write_out_mbox(k+2);
      while (spu_stat_in_mbox()==0);
      spu_read_in_mbox();
    }/*fin du for k*/
 }/*fin du for j*/
 
  /*envoi de la notification de fin*/
 while (spu_stat_out_mbox()==0);
 spu_write_out_mbox(-1);
 
  //printf("__SPE|%lld - écrit résultat\n",spe);
 /* DMA Transfer 5 : PUT output data */
 for(i=0;i<size_b;i++){
   mfc_put( out_C+(i*size_b), 
	    abs_params.out_matrix_C+
	    (i*abs_params.size_P*sizeof(t_complex))+
	    ((abs_params.size_P)*spenumber*size_b*sizeof(t_complex))+
	    (spenumber*size_b*sizeof(t_complex)),   
	    size_b * sizeof(t_complex), 
	    tag, tid, rid);
 }
 mfc_write_tag_mask(1<<tag);
 mfc_read_tag_status_all();
 
 return 0;
}
